--[[
副本逻辑总入口
接口列表：
applyCopyRoom	-- 申请副本房间
onEnter			-- 玩家进入场景回调
onLeave			-- 玩家离开场景回调
checkEnter		-- 检查副本进入点
roomBegin		-- 副本房间开始
roomFinish		-- 副本房间完成
roomClear		-- 副本房间回收
humanBeginCopy  -- 玩家开始副本
humanEndCopy	-- 玩家结束副本
onTimerTenSec	-- 定时器事件 10秒一次
onTimerOneSec	-- 定时器事件 1秒一次
getLeftTime		-- 获取副本剩余时间
getCopyHumanList -- 获得副本中的所有human
sendCopy		-- 副本广播
--]]

local Log = require("common.Log")
local LogDefine = require("common.LogDefine")
local Lang = require("common.Lang")
local ExcelMap = require("excel.Map")
local CopyConfig = ExcelMap.copy
local SceneConfig = ExcelMap.scene
local Map = require("core.Map")
local Msg = require("core.Msg")
local Obj = require("core.Obj")
local ObjHuman = require("core.ObjHuman")
local ObjItem = require("core.ObjItem")
local ObjPet = require("core.ObjPet")
local Timer = require("core.Timer")
local SceneEvent = require("core.SceneEvent")
local CopyDefine = require("copy.Define")
local Aim = require("copy.Aim")
local CopySingle = require("copy.CopySingle")
local CopyMulti = require("copy.CopyMulti")
local CopyTeam = require("copy.CopyTeam")
local CopyFamily = require("copy.CopyFamily")
local CopyCouple = require("copy.CopyCouple")
local CopyReward = require("copy.CopyReward")
local CopyCallback = require("copy.CallbackL")
local HideCollect = require("collect.HideCollect")
local TaskCallbacks= require("task.TaskCallbacks")
local ActivityLogicL = require("activity.ActivityLogicL")
local CopyCommon = require("copy.CopyCommon")
local JZWar = require("copy.JZWar")
local OperateActFL = require("activity.OperateActFL")
local OperateActDefine = require("activity.Define")


copyList = copyList or {}								-- 副本列表	copyList[copyID][room][mapID] = sceneID
mapID2CopyID = mapID2CopyID or {}						-- 地图ID对应副本ID
sceneID2CopyID = sceneID2CopyID or {}					-- 场景ID对应副本ID
sceneID2Room = sceneID2Room or {}						-- 场景ID对应房间号

roomDatas = roomDatas or {}								-- 房间数据 roomDatas[copyID][room]	{onlineNum=0,clearTime=清除时间（单位豪秒）}
copyHuman2Room = copyHuman2Room or {}					-- uuid->roomID

group2Copy = group2Copy or {}   -- 组ID与COPYID

room2Script = room2Script or {}   -- 房间ID和剧情脚本完成情况

TIME_CLEAR_COPY_ROOM_NO_HUMAN = 60000							-- 1分钟没人就清除
TIME_CLEAR_COPY_ROOM_FINISH = 210000							-- 副本结束后3.5分钟才回收


local logic_id = _G.logic_id

-- 初始化副本
function init()
    -- 初始化副本ID对应的地图列表
    if CopyConfig == nil then
        CopyConfig = ExcelMap.copy
    end
    if SceneConfig == nil then
        SceneConfig = ExcelMap.scene
    end
    for copyID,config in pairs(CopyConfig) do
        local roomCount = SceneConfig[config.mapList[1]].scene_count
        local logicid   = SceneConfig[config.mapList[1]].logic_id
        for _,mapID in ipairs(config.mapList) do
            assert(not mapID2CopyID[mapID], mapID)
            mapID2CopyID[mapID] = copyID
            if roomCount ~= SceneConfig[mapID].scene_count then
                assert(nil, "copy init error, mapID:" .. mapID .. " scenecount error")
            end
            if logicid ~= SceneConfig[mapID].logic_id then
                assert(nil, "maplist not in same logic: mapid:".. mapID.. " copyID="..copyID)
            end
        end
        group2Copy[config.group] = group2Copy[config.group] or {}
        group2Copy[config.group][#group2Copy[config.group] + 1] = copyID
    end

    local room = 1
    local copyRoomIndex = {}
    for mapID,sceneSize in pairs(Map.map_id_to_scene_id) do

        local copyID = mapID2CopyID[mapID]

        if copyID and SceneConfig[mapID].logic_id == logic_id then
            local tempRoom = room
            if copyRoomIndex[copyID] == nil then
                copyRoomIndex[copyID] = room
            else
                tempRoom = copyRoomIndex[copyID]
            end

            local min = sceneSize[1]
            local max = sceneSize[2]

            for sceneID=min,max do
                sceneID2CopyID[sceneID] = copyID
                sceneID2Room[sceneID] = tempRoom

                --初始化副本列表
                copyList[copyID] = copyList[copyID] or {}
                copyList[copyID][tempRoom] = copyList[copyID][tempRoom] or {}
                copyList[copyID][tempRoom][mapID] = sceneID

                tempRoom = tempRoom + 1
                room = room + 1
            end
        end
    end

    print("--- Copy Init ---")
end

function getCopyLogicID(copyID)
    local config = CopyConfig[copyID]
    return SceneConfig[config.mapList[1]].logic_id
end

-- 切换到其它线程
function checkCrossCopyRoom( human,copyID, startTime)
    if copyList[copyID] and (CopyConfig[copyID].type == CopyDefine.COPY_TYPE_SINGLE or CopyConfig[copyID].type == CopyDefine.COPY_TYPE_MULTI) then
        return  -- 同线程的单人副本不需要切换
    end
    local mm = Msg.lw.LW_CROSS_ROOM_APPLY
    mm.uuid = human._id
    mm.copyid = copyID
    mm.startTime = startTime or Timer.now
    Msg.send2world(mm, human._id)
    return true
end


-- 申请副本房间
function applyCopyRoom(copyID, uuid, startTime)
    local config = CopyConfig[copyID]
    if config == nil then return nil end

    roomDatas[copyID] = roomDatas[copyID] or {}
    if copyID == 419 then  -- for tmp
        for i = 420, 424 do
            if i ~= 421 then
                 roomDatas[i] = roomDatas[i] or {}
            end
        end       
    end

    if config.type == CopyDefine.COPY_TYPE_SINGLE then -- 单人副本
        return CopySingle.applyCopyRoomSingle(copyID, config, uuid)
    elseif config.type == CopyDefine.COPY_TYPE_TEAM then -- 组队副本
        return CopyTeam.applyCopyRoomTeam(copyID, config, uuid)
    elseif config.type == CopyDefine.COPY_TYPE_MULTI then -- 多人副本
        return CopyMulti.applyCopyRoomMulti(copyID, config, uuid)
    elseif config.type == CopyDefine.COPY_TYPE_FAMILY then -- 军团副本
        return CopyFamily.applyCopyRoomFamily(copyID, config, uuid)
    elseif config.type == CopyDefine.COPY_TYPE_COUPLE then -- 夫妻副本
        return CopyCouple.applyCopyRoomCouple(copyID, config, uuid)
    end

    return nil
end

function onEnter(obj, sceneID)
    if obj.obj_type ~= Obj.TYPE_HUMAN then return end
    if not isCopyBySceneID(sceneID) then return end

    local copyID = sceneID2CopyID[sceneID]
    local room = sceneID2Room[sceneID]
    if roomDatas[copyID] then
        local roomData = roomDatas[copyID][room]
        roomData.onlineNum = roomData.onlineNum+1
        Aim.sendAimListByHuman(obj, copyID, room)
    end

    local config = CopyConfig[copyID]
    if config.type == CopyDefine.COPY_TYPE_TEAM then -- 组队副本
        CopyTeam.onEnter(obj, sceneID)
    end
    CopyCallback.onHumanEnterCopyScene(obj, copyID, room, sceneID)
end

function onLeave(obj,sceneID)
    if obj.obj_type ~= Obj.TYPE_HUMAN then return end
    if not isCopyBySceneID(sceneID) then return end

    local copyID = sceneID2CopyID[sceneID]
    local room = sceneID2Room[sceneID]
    if roomDatas[copyID] then
        local roomData = roomDatas[copyID][room]
        roomData.onlineNum = roomData.onlineNum-1
    end

    local config = CopyConfig[copyID]
    if config.type == CopyDefine.COPY_TYPE_TEAM then -- 组队副本
        CopyTeam.onLeave(obj, sceneID)
    end

    CopyCallback.onHumanLeaveCopyScene(obj, copyID, room, sceneID)

    --离开场景设置玩家为非战斗状态。
    ObjHuman.resetFightSec(obj)
end

function isCopy(mapID)
    return mapID2CopyID[mapID]
end

function isCopyBySceneID(sceneID)
    return sceneID2CopyID[sceneID]
end

-- 获得FB结束时返回点
function getBackHomePoint(human, copyID)
    local config = CopyConfig[copyID]

    if config then
        local pointConf = config.backHome
        if pointConf[1] then -- 有配置回城点信息
        	local homeMapID = Map.getHomeMapID()
			if pointConf[1] == homeMapID and not pointConf[2] then
				return homeMapID, Map.getHomePoint(homeMapID)
			else
				return pointConf[1], pointConf[2], pointConf[3]
			end
        end
    end

    return human.db.copyEnterMap, human.db.copyEnterX, human.db.copyEnterY
end

-- 检查副本进入
function checkEnter(human, mapID, x, y)
    local roomID = copyHuman2Room[human._id]
    local copyID = mapID2CopyID[mapID]
    local backHomeMapId, backHomeX, backHomeY = getBackHomePoint(human, copyID)

    if roomID == nil then
        -- 副本房间已经回收了 退回外部入口地图
        if copyID == CopyDefine.COPY_JZWAR_ID then
            JZWar.onHumanCtl(human)
        end
        return backHomeMapId, nil, backHomeX, backHomeY
    end

    if roomDatas[copyID][roomID].finishCode ~= nil then
        -- 副本房间已经结束了 出去就不能再进入了
        if copyID == CopyDefine.COPY_JZWAR_ID then
            JZWar.onHumanCtl(human)
        end
        return backHomeMapId, nil, backHomeX, backHomeY
    end

    -- 副本房间有效
    local sceneID = copyList[copyID][roomID][mapID]
    return mapID, sceneID, x, y
end

-- 副本房间开始
function roomBegin(copyID, room)
    CopyCallback.onRoomBegin(copyID, room)
end

-- 副本房间完成
function roomFinish(copyID, room, finishCode, finishStr)
    if not roomDatas[copyID] or not roomDatas[copyID][room] then
      assert(nil, copyID, room, _G.logic_id)
      return
    end
    if roomDatas[copyID][room].finishCode then return end

    roomDatas[copyID][room].finish_time = Timer.now
    roomDatas[copyID][room].finishCode = finishCode

    local config = CopyConfig[copyID]
    local msgRet = Msg.gc.GC_COPY_FINISH
    msgRet.copyID = copyID
    msgRet.retCode = finishCode
    msgRet.retMsg = config.name .. " " .. finishStr

    -- 设置清除flag
    roomDatas[copyID][room].needSceneClear = true

    -- 下发副本结束通知
    sendCopy(msgRet, copyID, room)

    if finishCode == CopyDefine.COPY_FINISH_OK then
        CopyReward.setReward(copyID, room)

        local humanList = getCopyHumanList(copyID, room)
        for i = 1, #humanList do
            TaskCallbacks.taskCopyCB(humanList[i], copyID)
            ActivityLogicL.updateActivityValue(humanList[i], "copy", config.group)
        end
    end

    CopyCallback.onRoomFinish(copyID, room, finishCode)

end

-- 副本房间回收
function roomClear(copyID,room)
    local mapList = copyList[copyID][room]
    for mapID,sceneID in pairs(mapList)	do
        if Obj.scenes[sceneID] then
            -- 如果还有人在副本中把人踢出副本
            for _, human in pairs(Obj.scenes[sceneID]) do
                if human.obj_type == Obj.TYPE_HUMAN then
                    local backHomeMapId, backHomeX, backHomeY = getBackHomePoint(human, copyID)
                    ObjHuman.enterMap(human, backHomeMapId, backHomeX, backHomeY)
                end
            end

            -- 重置地图事件 停止ai
            SceneEvent.sceneClear(sceneID)

            -- 因为道具独享掉落里面并没有加入场景 所以这里要再处理一把
            ObjItem.clearOwnerItemByScene(sceneID)
        end

        HideCollect.clearScene(sceneID)
    end
    CopyCallback.onRoomClear(copyID, room)
    roomDatas[copyID][room] = nil -- 注意 这个要在copyteam的回调之前 否则有些状态不对
    if room2Script[copyID] and room2Script[copyID][room] then --清附剧情脚本完成情况
        room2Script[copyID][room] = nil
    end
    for uuid, roomID in pairs(copyHuman2Room) do
        if roomID == room then
            copyHuman2Room[uuid] = nil
        end
    end
    print("-- roomClear copy room --")
end

-- 玩家开始副本
function humanBeginCopy(human, copyID, room, x,y, args)
    local __, nMapID = CopyCommon.specialCtl(human, copyID) 
    local args = nil
    if nMapID then
        args = {mapID = nMapID} 
    end

    local copyConfig = CopyConfig[copyID]
    if copyConfig.type == CopyDefine.COPY_TYPE_SINGLE then -- 单人副本
        CopySingle.humanBeginCopy(human, copyID, room,x,y)
    elseif copyConfig.type == CopyDefine.COPY_TYPE_TEAM then -- 组队副本
        CopyTeam.humanBeginCopy(human, copyID, room)
    elseif copyConfig.type == CopyDefine.COPY_TYPE_MULTI then -- 多人副本
        CopyMulti.humanBeginCopy(human, copyID, room,x,y, args)
    elseif copyConfig.type == CopyDefine.COPY_TYPE_FAMILY then -- 军团副本
        CopyFamily.humanBeginCopy(human, copyID, room,x,y)
    elseif copyConfig.type == CopyDefine.COPY_TYPE_COUPLE then -- 夫妻副本
        CopyCouple.humanBeginCopy(human, copyID, room)
    end
    CopyCallback.onHumanBeginCopy(human, copyID, room)
    TaskCallbacks.taskCopyEnterCB(human, copyID)
    -- 写副本进入日志
    Log.write(Log.LOGID_OSS_COPY, Log.makeHumanInfo(human), copyID, copyConfig.group, CopyCommon.getCopyEnterCount(human, copyID), LogDefine.COPY_ACTION.BEGIN, 0, Aim.getAimProgress(copyID, room))
end

-- 玩家结束副本
function humanEndCopy(human, copyID, roomID, args)
    local config = CopyConfig[copyID]
    local copyStatus = roomDatas[copyID][roomID].finishCode or 0

    CopyCallback.onHumanEndCopy(human, copyID, roomID)

    if human.db.hp < 1 then
        human.db.hp = 0x7fffffff
        ObjHuman.doCalc(human)
        local mm = Msg.gc.GC_REVIVE
        mm.obj_id = human.id
        mm.hp = human.db.hp
        mm.type = 1
        Obj.sendArea(mm, human.scene_id, human.x, human.y)
        ObjPet.callPet(human)
    end

    if config.type == CopyDefine.COPY_TYPE_SINGLE then -- 单人副本
        CopySingle.humanEndCopy(human)
    elseif config.type == CopyDefine.COPY_TYPE_TEAM then -- 组队副本
        CopyTeam.humanEndCopy(human)
    elseif config.type == CopyDefine.COPY_TYPE_MULTI then -- 多人副本
        CopyMulti.humanEndCopy(human, args)
    elseif config.type == CopyDefine.COPY_TYPE_FAMILY then -- 军团副本
        CopyFamily.humanEndCopy(human)
    elseif config.type == CopyDefine.COPY_TYPE_COUPLE then -- 夫妻副本
        CopyCouple.humanEndCopy(human)
    end

    -- 写副本退出日志
    Log.write(Log.LOGID_OSS_COPY, Log.makeHumanInfo(human), copyID, config.group, CopyCommon.getCopyEnterCount(human, copyID), LogDefine.COPY_ACTION.EXIT, copyStatus, Aim.getAimProgress(copyID, roomID))
end

local needClearRoom = {}

-- 定时器事件 1秒一次
function onTimerOneSec()
    for copyID,roomList in pairs(roomDatas) do
        needClearRoom[0] = 0
        for room in pairs(roomList) do
            if roomDatas[copyID][room].finishCode == nil then
                CopyCallback.onTimerOneSec(copyID,room)
            end

            if roomDatas[copyID][room] and roomDatas[copyID][room].needSceneClear then
                roomDatas[copyID][room].needSceneClear = nil
                local mapList = copyList[copyID][room]
                for mapID, sceneID in pairs(mapList) do
                    if Obj.scenes[sceneID] then
                        -- 重置地图事件
                        SceneEvent.sceneClearExcepteHumanAndNpc(sceneID)
                    end
                end
                CopyCallback.onRoomFinish2(copyID, room)
            end

            if roomDatas[copyID][room] and roomDatas[copyID][room].clearTime and Timer.now >= roomDatas[copyID][room].clearTime then
                needClearRoom[0] = needClearRoom[0] + 1
                needClearRoom[needClearRoom[0]] = room
            end
        end
        for i = 1, needClearRoom[0] do
            roomClear(copyID, needClearRoom[i])
        end
    end
end

-- 定时器事件 10秒一次
function onTimerTenSec()
    for copyID,roomList in pairs(roomDatas) do
        needClearRoom[0] = 0
        local config = CopyConfig[copyID]
        for room,roomData in pairs(roomList) do
            if roomData.finish_time and TIME_CLEAR_COPY_ROOM_FINISH < Timer.now - roomData.finish_time then
                needClearRoom[0] = needClearRoom[0] + 1
                needClearRoom[needClearRoom[0]] = room
            else
                if roomDatas[copyID][room].finishCode == nil then
                    CopyCallback.onTimerTenSec(copyID,room)
                end
                if config.time > 0 and Timer.now - roomData.startTime > ((config.time + 20) * 1000) then
                    needClearRoom[0] = needClearRoom[0] + 1
                    needClearRoom[needClearRoom[0]] = room
                elseif config.time > 0 and Timer.now - roomData.startTime > (config.time * 1000) then
                    roomFinish(copyID, room, CopyDefine.COPY_FINISH_TIMEOUT, Lang.COPY_FINISH_TIMEOUT)
                else
                    if roomData.onlineNum > 0 then
                        roomData.clearTime = nil
                    else
                        -- 副本没人
                        if roomData.clearTime == nil then
                            roomData.clearTime = Timer.now + TIME_CLEAR_COPY_ROOM_NO_HUMAN
                            if (config.type == CopyDefine.COPY_TYPE_MULTI or config.type == CopyDefine.COPY_TYPE_FAMILY) and config.noManThenFinish == 0 then
                                roomData.clearTime = nil
                            end
                        end
                    end
                end
            end
        end
        for i = 1, needClearRoom[0] do
            roomClear(copyID, needClearRoom[i])
        end
    end
end

-- 获取副本剩余时间
function getLeftTime(copyID, roomID)
    local config = CopyConfig[copyID]
    local leftTime, totalTime = CopyCallback.onGetLeftTime(copyID, roomID)
    if leftTime then
      if not totalTime then
          totalTime = config.time
      end
      return leftTime, totalTime
    end
    if config.time == 0 then return 0,0 end

    return math.floor((roomDatas[copyID][roomID].startTime + config.time * 1000 - Timer.now) / 1000), config.time
end

-- 获得副本中的所有human
function getCopyHumanList(copyID, roomID)
    local humanList = {}
    local mapList = copyList[copyID][roomID]
    for mapID,sceneID in pairs(mapList)	do
        if Obj.scenes[sceneID] then
            for _, human in pairs(Obj.scenes[sceneID]) do
                if human.obj_type == Obj.TYPE_HUMAN then
                    humanList[#humanList + 1] = human
                end
            end
        end
    end

    return humanList
end

-- 获得副本中的所有human
function getHumanListByCopyID(copyID)
    local humanList = {}
    local copyData = copyList[copyID]

    for roomID, mapList in pairs(copyData) do
        for mapID,sceneID in pairs(mapList)	do
            if Obj.scenes[sceneID] then
                for _, human in pairs(Obj.scenes[sceneID]) do
                    if human.obj_type == Obj.TYPE_HUMAN then
                        humanList[#humanList + 1] = human
                    end
                end
            end
        end
    end

    return humanList
end

function sendCopy(msg, copyID, roomID)
    local list = Msg.list
    local len = 0
    for _,sceneID in pairs(copyList[copyID][roomID])	do
        local scene = Obj.scenes[sceneID]
        if scene then
            for _, human in pairs(scene) do
                local fd = human.fd
                if fd then
                    len = len + 1
                    list[len] = fd
                end
            end
        end
    end
    if 0 < len then
        list[0] = len
        Msg.sendMulti(msg, list)
    end
end

function getSceneIDByDetail(mapID, copyID, roomID)
    if copyList[copyID] == nil then return end
    if copyList[copyID][roomID] == nil then return end
    return copyList[copyID][roomID][mapID]
end

function copyScriptStop(copyID, roomID, script)
    room2Script[copyID] =  room2Script[copyID] or {}
    room2Script[copyID][roomID] = room2Script[copyID][roomID] or {}
    room2Script[copyID][roomID][script] = 1
end


function getXunbaoCostCnt(human)
    local cnt = CopyCommon.getCopyEnterCount(human, CopyDefine.COPY_XUNBAO_COPY_ID)
    cnt = cnt + 1
    if cnt > CopyCommon.getCopyMaxEnterCount(human, CopyDefine.COPY_XUNBAO_COPY_ID) then
        cnt = CopyCommon.getCopyMaxEnterCount(human, CopyDefine.COPY_XUNBAO_COPY_ID)
    end
    return 2^(cnt-1)
end

function getXunbaoAddDropRate(human)
    if isCopyBySceneID(human.scene_id) ~= CopyDefine.COPY_XUNBAO_COPY_ID then
        return 1
    end
    local cnt = CopyCommon.getCopyEnterCount(human, CopyDefine.COPY_XUNBAO_COPY_ID)
    if cnt > CopyCommon.getCopyMaxEnterCount(human, CopyDefine.COPY_XUNBAO_COPY_ID) then
        cnt = CopyCommon.getCopyMaxEnterCount(human, CopyDefine.COPY_XUNBAO_COPY_ID)
    end
    if cnt <= 0 then cnt = 1 end

    if OperateActFL.onCheckActIsRunning(OperateActDefine.OPERATEACT_TYPE_DUOBAODOUBLE) then --双倍运营活动
        cnt = cnt + 1
    end
    return cnt
end
